package mcp

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"workflow-gui/internal/database"
	"workflow-gui/internal/httpclient"
	"workflow-gui/internal/mcp/tools"
	"workflow-gui/internal/protocol"
	"workflow-gui/internal/transport"
)

// Server represents the MCP server
type Server struct {
	transport  *transport.StdioTransport
	registry   *tools.Registry
	handler    *tools.Handler
	guiURL     string
	guiProcess *exec.Cmd
}

// NewServer creates a new MCP server
func NewServer() *Server {
	guiURL := "http://localhost:9081"

	// Initialize database
	exePath, err := os.Executable()
	if err != nil {
		log.Printf("Warning: Failed to get executable path: %v", err)
		return newServerWithoutDB(guiURL, ".")
	}

	exeDir := filepath.Dir(exePath)
	dbPath := filepath.Join(exeDir, "..", "data", "workflow.db")
	projectRoot := filepath.Join(exeDir, "..")

	db, err := database.Open(database.Config{Path: dbPath})
	if err != nil {
		log.Printf("Warning: Failed to open database: %v", err)
		return newServerWithoutDB(guiURL, projectRoot)
	}

	registry := tools.NewRegistry()
	handler, err := tools.NewHandler(registry, guiURL, db, projectRoot)
	if err != nil {
		log.Printf("Warning: Failed to create handler: %v", err)
		return newServerWithoutDB(guiURL, projectRoot)
	}

	return &Server{
		transport: transport.NewStdioTransport(),
		registry:  registry,
		handler:   handler,
		guiURL:    guiURL,
	}
}

// newServerWithoutDB creates a server without database (fallback)
func newServerWithoutDB(guiURL string, projectRoot string) *Server {
	registry := tools.NewRegistry()
	handler, _ := tools.NewHandler(registry, guiURL, nil, projectRoot)

	return &Server{
		transport: transport.NewStdioTransport(),
		registry:  registry,
		handler:   handler,
		guiURL:    guiURL,
	}
}

// Start starts the MCP server
func (s *Server) Start() error {
	log.Println("Starting Workflow Manager MCP Server")

	// Ensure GUI service is running
	if err := s.ensureGUIServiceRunning(); err != nil {
		log.Printf("Warning: Failed to start GUI service: %v", err)
	}

	// Main message loop
	for {
		msg, err := s.transport.ReadMessage()
		if err != nil {
			if err == io.EOF {
				log.Println("Client disconnected")
				return nil
			}
			log.Printf("Error reading message: %v", err)
			continue
		}

		if err := s.handleMessage(msg); err != nil {
			log.Printf("Error handling message: %v", err)
		}
	}
}

// ensureGUIServiceRunning ensures the GUI service is running
func (s *Server) ensureGUIServiceRunning() error {
	client := httpclient.NewClient(s.guiURL)

	// Check if already running
	if err := client.HealthCheck(); err == nil {
		log.Println("GUI service is already running")
		return nil
	}

	// Start GUI service
	log.Println("Starting GUI service...")

	exePath, err := os.Executable()
	if err != nil {
		return fmt.Errorf("failed to get executable path: %w", err)
	}

	exeDir := filepath.Dir(exePath)
	guiExe := filepath.Join(exeDir, "workflow-gui-server.exe")

	s.guiProcess = exec.Command(guiExe)
	s.guiProcess.Stdout = os.Stderr
	s.guiProcess.Stderr = os.Stderr

	if err := s.guiProcess.Start(); err != nil {
		return fmt.Errorf("failed to start GUI service: %w", err)
	}

	// Wait for GUI service to be ready
	for i := 0; i < 20; i++ {
		time.Sleep(500 * time.Millisecond)
		if err := client.HealthCheck(); err == nil {
			log.Println("GUI service started successfully")
			return nil
		}
	}

	return fmt.Errorf("GUI service failed to start within 10 seconds")
}

// handleMessage handles a JSON-RPC message
func (s *Server) handleMessage(msg *protocol.JSONRPCMessage) error {
	// Handle requests
	if msg.Method != "" {
		return s.handleRequest(msg)
	}

	return nil
}

// handleRequest handles a JSON-RPC request
func (s *Server) handleRequest(msg *protocol.JSONRPCMessage) error {
	switch msg.Method {
	case "initialize":
		return s.handleInitialize(msg)
	case "tools/list":
		return s.handleToolsList(msg)
	case "tools/call":
		return s.handleToolsCall(msg)
	default:
		return s.transport.WriteError(msg.ID, protocol.MethodNotFound, "Method not found")
	}
}

// handleInitialize handles the initialize request
func (s *Server) handleInitialize(msg *protocol.JSONRPCMessage) error {
	result := protocol.InitializeResult{
		ProtocolVersion: protocol.ProtocolVersion,
		Capabilities: protocol.Capabilities{
			Tools: map[string]interface{}{},
		},
		ServerInfo: protocol.ServerInfo{
			Name:    "workflow-manager",
			Version: "1.0.0",
		},
	}

	return s.transport.WriteResult(msg.ID, result)
}

// handleToolsList handles the tools/list request
func (s *Server) handleToolsList(msg *protocol.JSONRPCMessage) error {
	result := protocol.ToolsListResult{
		Tools: s.registry.GetTools(),
	}

	return s.transport.WriteResult(msg.ID, result)
}

// handleToolsCall handles the tools/call request
func (s *Server) handleToolsCall(msg *protocol.JSONRPCMessage) error {
	var req protocol.ToolCallRequest
	if err := json.Unmarshal(msg.Params, &req); err != nil {
		return s.transport.WriteError(msg.ID, protocol.InvalidParams, "Invalid parameters")
	}

	// Execute tool
	result, err := s.handler.HandleToolCall(req.Name, req.Arguments)
	if err != nil {
		return s.transport.WriteError(msg.ID, protocol.InternalError, err.Error())
	}

	return s.transport.WriteResult(msg.ID, result)
}
